package com.zmn.uac.util;

import com.zmn.consts.GlobalConsts;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.lang3.StringUtils;
import sun.misc.BASE64Decoder;
import sun.misc.BASE64Encoder;

import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;

/**
 * @title: AES加解密工具类
 * @author:Rananquan
 * @Date:2022/5/12 10:42
 */
@Slf4j
public class AesUtil {

    public static final String KEY = "vjLrEuwwKTGXq/3L24SElZUN8X0V9MYzk6NXaWLSBV4=";

    /**
     * 生成Key对象
     * @return
     * @throws NoSuchAlgorithmException
     */
    public static KeyGenerator getKeyGenerator() throws NoSuchAlgorithmException {
        KeyGenerator keyGenerator = KeyGenerator.getInstance("AES");
        keyGenerator.init(256);

        return keyGenerator;
    }


    /**
     * 生成AES密钥
     * @return 密钥
     */
    public static String generateKey(){
        String key = GlobalConsts.SPLITER_EMPTY;
        try {
            KeyGenerator keyGenerator = getKeyGenerator();
            SecretKey secretKey = keyGenerator.generateKey();
            byte[] encoded = secretKey.getEncoded();
            key =  Base64.encodeBase64String(encoded);
        }catch (Exception e){
            e.printStackTrace();
        }

        return key;
    }


    /**
     * AES加密
     * @param plainText 明文
     * @param key 密钥
     * @return 该字符串的AES密文值
     */
    public static String encrypt(String plainText, String key) {
        if (StringUtils.isEmpty(key)|| StringUtils.isEmpty(plainText)) {
            return null;
        }

        try {
            key = getMD5(key);
            assert key != null;
            byte[] raw = key.getBytes(StandardCharsets.UTF_8);
            SecretKeySpec skySpec = new SecretKeySpec(raw, "AES");
            Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
            // 使用CBC模式，需要一个向量iv，可增加加密算法的强度
            IvParameterSpec iv = new IvParameterSpec(key.getBytes(StandardCharsets.UTF_8));
            cipher.init(Cipher.ENCRYPT_MODE, skySpec, iv);
            byte[] encrypted = cipher.doFinal(plainText.getBytes(StandardCharsets.UTF_8));

            // 此处使用BASE64做转码功能，同时能起到2次加密的作用。
            return new BASE64Encoder().encode(encrypted);
        } catch (Exception ex) {
            log.error("AES加密失败");
            ex.printStackTrace();

            return GlobalConsts.SPLITER_EMPTY;
        }
    }


    /**
     * AES解密
     * @param cipherText 密文
     * @param key 密钥
     * @return 该密文的明文
     */
    public static String decrypt(String cipherText, String key) {
        if (StringUtils.isEmpty(key)|| StringUtils.isEmpty(cipherText)) {
            return GlobalConsts.SPLITER_EMPTY;
        }
        try {
            key=getMD5(key);
            assert key != null;
            byte[] raw = key.getBytes(StandardCharsets.UTF_8);
            SecretKeySpec skySpec = new SecretKeySpec(raw, "AES");
            Cipher cipher=Cipher.getInstance("AES/CBC/PKCS5Padding");
            // 使用CBC模式，需要一个向量iv，可增加加密算法的强度
            IvParameterSpec iv = new IvParameterSpec(key.getBytes(StandardCharsets.UTF_8));
            cipher.init(Cipher.DECRYPT_MODE, skySpec, iv);

            // 先用base64解密
            byte[] encrypted = new BASE64Decoder().decodeBuffer(cipherText);
            try {
                byte[] original = cipher.doFinal(encrypted);
                return new String(original, StandardCharsets.UTF_8);
            } catch (Exception e) {
                log.error("AES解密失败");
                e.printStackTrace();

                return GlobalConsts.SPLITER_EMPTY;
            }
        } catch (Exception ex) {
            log.error("AES解密失败");
            ex.printStackTrace();

            return GlobalConsts.SPLITER_EMPTY;
        }
    }

    /**
     * 进行MD5加密
     * @param s 要进行MD5转换的字符串
     * @return 该字符串的MD5值的8-24位
     */
    public static String getMD5(String s){
        char[] hexDigits ={'0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z'};

        try {
            byte[] btInput = s.getBytes();
            // 获得MD5摘要算法的 MessageDigest 对象
            MessageDigest mdInst = MessageDigest.getInstance("MD5");
            // 使用指定的字节更新摘要
            mdInst.update(btInput);
            // 获得密文
            byte[] md = mdInst.digest();
            // 把密文转换成十六进制的字符串形式
            int j = md.length;
            char[] str = new char[j * 2];
            int k = 0;
            for (byte byte0 : md) {
                str[k++] = hexDigits[byte0 >>> 4 & 0xf];
                str[k++] = hexDigits[byte0 & 0xf];
            }

            return new String(str).substring(8,24);
        } catch (Exception e) {
            log.error("MD5加密失败");
            e.printStackTrace();

            return null;
        }
    }


    public static void main(String[] args) {
        System.out.println("密钥："+KEY);

        String str = "15523250563";
        System.out.println("原文："+str);

        String encrypt = encrypt(str, KEY);
        System.out.println("加密后的内容："+encrypt);

        String decrypt = decrypt(encrypt, KEY);
        System.out.println("解密后的内容："+decrypt);
    }
}
